﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using Web = System.Web.UI.WebControls;
using System.Security.Permissions;
using System.IO;
using System.Drawing.Design;
using JQuery.Net.Controls;

namespace JQuery.Net.UI
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ToolboxData("<{0}:Menu runat=server></{0}:Menu>")]
    public class Menu
        : jQNetControl
    {
        #region constructor
        public Menu()
            : base(HtmlTextWriterTag.Div)
        {
            Items = new MenuItemCollection();
            /// Default Settings that can not be set via the Default Value Attribute
            Direction = Web.RepeatDirection.Vertical;
            SourceType = DataSourceType.local;
            ContentFlow = ContentFlowType.none;
        }
        #endregion

        #region properties
        [DisplayName("Content Flow"), Description("Determines how the element will flow on the page, IE Float: inherit|none|left|right."), Category("Appearance"), Browsable(true)]
        public ContentFlowType ContentFlow { get; set; }
        [DisplayName("Is Context Menu"), Description("Determines the use of the menu if it is a context right click menu or regular menu."), Category("Behavior"), Browsable(true)]
        public bool IsContextMenu { get; set; }
        /// <summary>
        /// Get or Set the repeat direction of the menu control
        /// </summary>
        [DisplayName("Direction"), Description("Layout Direction"), Category("Behavior"), Browsable(true), Bindable(false)]
        public Web.RepeatDirection Direction { get; set; }
        /// <summary>
        /// Get or Set the Menu Items Collection
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerProperty), MergableProperty(false)]
        public MenuItemCollection Items { get; set; }

        public Web.Unit Width { get; set; }
        #endregion

        public override void RegisterJQNScriptInclude(ResourceManager manager, JQNRegistration options)
        {
            base.RegisterJQNScriptInclude(manager, options);

            manager.Add(new Resource(Resources.JQN.ui_menu, GetType(), Resource.ContentTypes.javascript) { Ordinal = 1 });
        }

        public override void RegisterJQNScriptStartup(StringWriter oWriter)
        {
            List<MenuItem> oItemsWithScripts = Items.GetItemsWithEvents;

            StringBuilder oOptions = new StringBuilder("{");

            oOptions.AppendFormat("callback: {0}", SourceType == DataSourceType.callback ? "true" : "false");

            if (oItemsWithScripts.Count > 0)
            {
                oOptions.AppendFormat(", actions: [{0}]", GenerateActionScripts(oItemsWithScripts));
            }

            oOptions.Append("}");

            oWriter.Write("\t");
            // the below js method is the preferred method for calling a widget. this avoids the namespace conflict that can occur with the jquery library
            // considering when it comes to jquery's widgets it is last in first out.
            oWriter.WriteLine(String.Format(
@"$('#{0}').widget('jqn.menu', {1});"
                ,/*{0}*/ClientID
                ,/*{1}*/oOptions.Length > 0 ? oOptions.ToString() : String.Empty));
        }

        private string GenerateActionScripts(List<MenuItem> oItemsWithScripts)
        {
            StringBuilder oActionArray = new StringBuilder();

            foreach (MenuItem oItem in oItemsWithScripts)
            {
                string identifier = null;

                if (oItem.Command.IsNotNullOrEmpty())
                {
                    identifier = String.Format("[cmd={0}]", oItem.Command);
                }
                else
                {
                    identifier = String.Format(":contains('{0}')", oItem.Text);
                }

                Script oScript = oItem.ScriptEvents.Single((X) => X.Event == jQNetEvents.click);

                oActionArray.Append("{").AppendFormat(" match: \"a{0}\", func: {1} ", identifier, oScript.Body).AppendLine("},");
            }

            return oActionArray.Remove((oActionArray.Length - 1), 1).ToString();
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (!Page.IsCallback)
            {
                CSSRegistration.Registration(new Resource(Resources.Css.ui_menu, null, typeof(Resource), Resource.ContentTypes.css));
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            /// this menu is only visible if it is flagged visible and root elements are present
            Visible = Visible && Items.Count > 0;

            Attributes["role"] = "menu";

            CssClass = "menu ui-widget ui-widget-content ui-widget-header".Join(" "
                , (IsContextMenu ? "context" : String.Empty)
                , (Direction == Web.RepeatDirection.Vertical ? Web.RepeatDirection.Vertical.ToString().ToLower() : String.Empty)
                , CssClass).Trim();

            if (!IsContextMenu)
            {
                Style.Add("clear", "both");
                Style.Add("display", "block");
            }

            if (!Width.IsEmpty)
            {
                Style.Add("width", Width.ToString());
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            RenderBeginTag(writer);

            RenderMenu(writer, Items);

            if (!IsContextMenu && ContentFlow != ContentFlowType.none)
            {
                if (ContentFlow == ContentFlowType.inherit)
                    writer.AddStyleAttribute("clear", "both");
                else
                    writer.AddStyleAttribute("clear", ContentFlow.ToString());
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
                writer.RenderEndTag();
            }

            RenderEndTag(writer);
        }

        protected virtual void RenderMenu(HtmlTextWriter oWriter, MenuItemCollection oItems)
        {
            oWriter.AddAttribute("role", "menubar");
            oWriter.AddAttribute("lvl", oItems.Owner.IsNotNull(1, (X) => (X.Depth + 1)).ToString());

            if (oItems.Owner != null)
            {
                oWriter.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");

                oWriter.WriteLine();
            }
            else if(ContentFlow != ContentFlowType.none)
            {
                oWriter.AddStyleAttribute("float", ContentFlow.ToString());
            }

            oWriter.RenderBeginTag(HtmlTextWriterTag.Ul);

            foreach (MenuItem oItem in oItems)
            {
                oWriter.AddAttribute("role", oItem.Type.ToString().ToLower());

                if (oItem.IsRootElement && Direction == Web.RepeatDirection.Horizontal)
                {
                    if (ContentFlow == ContentFlowType.inherit || ContentFlow == ContentFlowType.none)
                        ContentFlow = ContentFlowType.left;

                    oWriter.AddStyleAttribute("float", ContentFlow.ToString());
                }

                if (oItem.Type == MenuItemType.Seperator)
                {   //  add the seperator class if this menu item is a seperator
                    oWriter.AddAttribute(HtmlTextWriterAttribute.Class, oItem.Type.ToString().ToLower());
                }

                oWriter.RenderBeginTag(HtmlTextWriterTag.Li);

                RenderItemTemplate<MenuItem>(oWriter, oItem, false);

                if (oItem.Items.Count > 0 && SourceType == DataSourceType.local)
                {   /// we only render out the entire menu for local source type
                    oWriter.Indent++;
                    RenderMenu(oWriter, oItem.Items);
                    oWriter.Indent--;
                }

                oWriter.RenderEndTag();

                if(oItems.IndexOf(oItem) < (oItems.Count - 1))
                    oWriter.WriteLine();
            }

            oWriter.RenderEndTag();
        }

        protected override void RenderItemTemplate<T>(HtmlTextWriter oWriter, T item, bool isTemplate)
        {
            String 
                oValue = String.Empty,
                oType = item.ToItemTemplate("Type", isTemplate);

            oValue = item.ToItemTemplate("Url", isTemplate);
            oWriter.AddAttribute(HtmlTextWriterAttribute.Href, oValue.StartsWith("~") ? Page.ResolveUrl(oValue) : oValue, !isTemplate);
            oValue = item.ToItemTemplate("Target", isTemplate);
            oWriter.AddAttribute(HtmlTextWriterAttribute.Target, oValue, false);
            oValue = item.ToItemTemplate("ToolTip", isTemplate);
            if (oValue.IsNotNullOrEmpty())
            {
                oWriter.AddAttribute(HtmlTextWriterAttribute.Title, oValue, false);
            }
            oValue = item.ToItemTemplate("Command", isTemplate);
            if (oValue.IsNotNullOrEmpty())
            {
                oWriter.AddAttribute("cmd", oValue, false);
            }
            oValue = item.ToItemTemplate("Arguments", isTemplate);
            if (oValue.IsNotNullOrEmpty())
            {
                oWriter.AddAttribute("args", oValue, false);
            }

            oWriter.Indent++;
            oWriter.WriteLine();
            /// anchor portion of the template
            oWriter.RenderBeginTag(HtmlTextWriterTag.A);
            oWriter.Indent++;
            oWriter.WriteLine();

            MenuItem oItem = item as MenuItem;

            oValue = item.ToItemTemplate("Image", isTemplate);
            oWriter.AddAttribute(HtmlTextWriterAttribute.Class, String.Format("img {0}", isTemplate ? String.Empty : !(oValue.StartsWith("~") || oValue.StartsWith("/"))  ? oValue : String.Empty).Trim());

            if ((oValue.StartsWith("~") || oValue.StartsWith("/")))
            {
                oWriter.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, oValue.StartsWith("~") ? Page.ResolveUrl(oValue) : oValue);
            }
          
            /// span image anchor tag
            oWriter.RenderBeginTag(HtmlTextWriterTag.Span);
            oWriter.RenderEndTag();

            oWriter.AddAttribute(HtmlTextWriterAttribute.Class, String.Empty.Join(" ", "container", ((Direction == Web.RepeatDirection.Vertical || oItem.Depth > 1) ? Web.RepeatDirection.Vertical.ToString() : String.Empty).ToLower()).Trim());
            oWriter.RenderBeginTag(HtmlTextWriterTag.Span);
            /// span label tag
            oWriter.AddAttribute(HtmlTextWriterAttribute.Class, String.Format("label {0}", !oType.Eq(MenuItemType.MenuItem.ToString()) ? oType.ToJSProper() : String.Empty).Trim());
            oWriter.RenderBeginTag(HtmlTextWriterTag.Span);

            oValue = item.ToItemTemplate("Text", isTemplate);
            oWriter.Write(oValue.Trim());

            oWriter.RenderEndTag();

            if (!isTemplate)
                /// set the value to none when this is a horizontal menu bar for the root elements
                if ((Direction == Web.RepeatDirection.Horizontal && oItem.IsRootElement) || (oItem.Items.Count == 0))
                    oValue = "none";
                else
                    oValue = String.Empty;
            else
                oValue = "{%= flyout %}";

            oWriter.AddAttribute(HtmlTextWriterAttribute.Class, String.Format("flyout {0}", oValue).Trim());
            /// span flyout image
            oWriter.RenderBeginTag(HtmlTextWriterTag.Span);
            oWriter.RenderEndTag();
            oWriter.RenderEndTag();

            oWriter.AddStyleAttribute("clear", "both");
            oWriter.RenderBeginTag(HtmlTextWriterTag.Span);
            oWriter.RenderEndTag();

            oWriter.Indent--;
            oWriter.WriteLine();
            oWriter.RenderEndTag();
            oWriter.Indent--;
        }
    }
}
